package com.yancloud.android.reflection.get;

import android.content.Context;
import android.database.Cursor;
import android.os.IBinder;
import android.os.Parcel;
import android.util.Log;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.koushikdutta.async.http.Multimap;
import com.koushikdutta.async.http.server.AsyncHttpServerRequest;
import com.koushikdutta.async.http.server.AsyncHttpServerResponse;
import com.socks.library.KLog;
import com.yancloud.android.contractclient.CMClientHandler;
import com.yancloud.android.contractclient.ResultCallback;
import com.yancloud.android.contractclient.databaseHelper.DatabaseDao;
import com.yancloud.android.contractclient.databaseHelper.DatabaseHelper;
import com.yancloud.android.contractclient.databaseHelper.Do;
import com.yancloud.android.contractclient.nativesclient.WSHandler;
import com.yancloud.android.manager.IAPIBinder;
import com.yancloud.android.manager.IAppConnector;
import com.yancloud.android.manager.PackageVersionPair;
import com.yancloud.android.manager.URLRegx;


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

import ias.deepsearch.com.helper.service.DaemonService;
import ias.deepsearch.com.helper.ui.activity.HomeActivity;
import ias.deepsearch.com.helper.util.ShellUtils;

/**
 * Created by root on 6/27/17.
 */

public class CMDManager {
    private static final String PKG_NAME_ARGUMENT = "MODEL_ARGUMENT";
    private static final String KEY_INTENT = "KEY_INTENT";
    private static final String KEY_USER = "KEY_USER";
    private static final String TAG = "CMDManager";

    @Desc("callDoi")
    public static String callDOI(String doi, String args, String invoker) {
        DatabaseDao databaseDao = DatabaseDao.getInstance();
        JSONObject jsonObject = new JSONObject();
        if(databaseDao==null)
        {
            jsonObject.put("error","database 未初始化");
            return jsonObject.toJSONString();
        }
        Do doObject = databaseDao.getInfoByDoi(doi);

        //Log.d("1111","访问成功111111111111111111111111");
        Log.d(TAG,"***************************"+new Gson().toJson(doObject));
        if(doObject.getIsopen() == 1) {
            Log.d(TAG,"访问成功111111111111111111111111");
            String result = forward(doObject.getPackageName(), doObject.getMethodName(), args);
            jsonObject.put("code",1);
            jsonObject.put("result",result);
            //   String result = forward(doObject.getPackageName(), "getMethodDescriptions", args);
            databaseDao.increCallNumberByDoi(doi);

        } else{
            Log.d(TAG,"访问不成功111111111111111111111111");
            jsonObject.put("code",0);
            jsonObject.put("result","禁止访问");

        }
        return jsonObject.toJSONString();
    }
    @Desc("retriveCall")
    public static String retriveCall(){
        DatabaseDao databaseDao = DatabaseDao.getInstance();
        List<Do> list = databaseDao.getAllDoi();
        JSONArray json = (JSONArray) JSONArray.toJSON(list);
        return json.toJSONString();
    }
    @Desc("doiInfo")
    public static String doiInfo(String doi){
        DatabaseDao databaseDao = DatabaseDao.getInstance();
        JSONObject jsonObject = new JSONObject();
        Do doObject = databaseDao.getInfoByDoi(doi);
        if(doObject==null)
        {
            jsonObject.put("error","Handle不存在");
            return jsonObject.toJSONString();
        }
        String appname="";
        if(doObject.getPackageName().contains("starbucks"))
            appname+="starbucks";
        else if(doObject.getPackageName().contains("aweme"))
            appname+="aweme";
        jsonObject.put("Doi",doObject.getDoi());
        jsonObject.put("appname",appname);
        jsonObject.put("packageName",doObject.getPackageName());
        jsonObject.put("methodName",doObject.getMethodName());
        jsonObject.put("desp",doObject.getDesp());
        Log.d(doObject.getDoi(),appname+"      "+doObject.getPackageName()+"      "+doObject.getMethodName());
        return jsonObject.toJSONString();
    }
    @Desc("allApi")
    public static String allApi()
    {
        DatabaseDao databaseDao = DatabaseDao.getInstance();
        JSONObject jsonObject = new JSONObject();
        List<Do> data  = databaseDao.getAllDoi();
        if(data.size()<=0)
        {
            jsonObject.put("error","API信息未注册");
            return jsonObject.toJSONString();
        }
        int cnt=1;
        for(int i = 0;i<data.size();i++){

            JSONObject jsonObject1=new JSONObject();
            String appname="";
            if(data.get(i).getPackageName().contains("starbucks"))
                appname+="starbucks";
            else if(data.get(i).getPackageName().contains("aweme"))
                appname+="aweme";
            jsonObject1.put("Doi",data.get(i).getDoi());
            jsonObject1.put("appname",appname);
            jsonObject1.put("packageName",data.get(i).getPackageName());
            jsonObject1.put("methodName",data.get(i).getMethodName());
            jsonObject1.put("desp",data.get(i).getDesp());
            Log.d(data.get(i).getDoi(),data.get(i).getDoi()+"     "+data.get(i).getMethodName()+"       "+data.get(i).getDesp()+"       "+appname);
            jsonObject.put(String.valueOf(cnt),jsonObject1);
            cnt++;
            //list.add(map);
        }
        return jsonObject.toJSONString();
    }
    @Desc("list help info")
    public static String help(String args){
        Method[] methods = CMDManager.class.getDeclaredMethods();
        StringBuilder sb = new StringBuilder();
        for (Method m:methods){
            Desc desc = m.getAnnotation(Desc.class);
            if (desc==null)
                continue;
            sb.append(m.getName()+": ");
            sb.append(desc.value());
            sb.append("\n");
        }
        return sb.toString();
    }
    @Desc("update AppContract")
    public static String updateAppContract(String pkgName) {
        WSHandler.controller.contractDesp = null;
        WSHandler.controller.updateContract(new ResultCallback() {
            @Override
            public void onResult(String str) {
                System.out.print(str);
            }
        });
        return "success";
    }
    @Desc("kill process accroding to process name")
    public static String killApp(String pkgName){
        String sh = "ps";
        KLog.v("CMDMananger","killApp:"+pkgName);
        ShellUtils.CommandResult commandResult = ShellUtils.execCommand(sh,true);
        Scanner result = new Scanner(commandResult.successMsg);
        List<String> toKill = new ArrayList<String>();
        StringBuilder sb = new StringBuilder("Kill:"+pkgName+" PID\tPKG\n");
        try {
            result.nextLine();
            for (; result.hasNextLine(); ) {
                String line = result.nextLine();
                Scanner sc = new Scanner(line);
                //USER
                String temp;
                temp = sc.next();
                //PID
                String pid = sc.next();
                //PPID
                temp = sc.next();
                //VSIZE
                temp = sc.next();
                //WCHAN
                temp = sc.next();
                //PC
                temp = sc.next();
                temp = sc.next();
                temp = sc.next();
                String name = sc.next();
                if (name.contains(pkgName)) {
                    toKill.add(pid);
                    sb.append(pid).append("\t").append(name).append("\n");
                }            sc.close();
            }
        }catch(Exception e){
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(bo));
            sb.append(bo.toString());
        }
        result.close();
        for (String pid:toKill){
          sh = "kill -9 "+pid;
          ShellUtils.execCommand(sh,true);
        }
        KLog.v("CMDMananger","kill size:"+toKill.size());
        return sb.toString();
    }

    @Desc("list apps that running at foreground/background")
    public static String listPackages(String args){
        try {
            IBinder binder = DaemonService.instance.onBind(null);
            List<PackageVersionPair> list = IAppConnector.Stub.asInterface(binder).listPackages();
            return new Gson().toJson(list);
        } catch (Throwable e) {
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(bo));
            return bo.toString();
        }
    }
    static class TracingResult{
        String pkgName;
        String versionName;
        String tracingStatus;
    }



    @Desc("list apps tracing status that running at foreground/background")
    public static String listTracingStatus(String args){
        try {
            IBinder binder = DaemonService.instance.onBind(null);
            List<PackageVersionPair> list = IAppConnector.Stub.asInterface(binder).listPackages();
            List<TracingResult> ret = new ArrayList<>();
            for (PackageVersionPair pair:list){
                TracingResult tr = new TracingResult();
                tr.pkgName = pair.pkgName;
                tr.versionName = pair.versionName;
                tr.tracingStatus = forward(pair.pkgName,"isTracing","");
                ret.add(tr);
            }
            return new Gson().toJson(ret);
        } catch (Throwable e) {
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(bo));
            return bo.toString();
        }
    }

    @Desc("execute adb cmd")
    public static String adbCmd(String args){
        args =  URLDecoder.decode(args);
        ShellUtils.CommandResult commandResult = ShellUtils.execCommand(args, true);
        return new Gson().toJson(commandResult);
    }
    static Set<String> downloading = new HashSet<>();

    @Desc("upload files, param={url:__url__,fileName:__fileName__}")
    public static String uploadFiles(final String args){
        String urlAndFileName = URLDecoder.decode(args);
       final  Map<String,String> params = new Gson().fromJson(urlAndFileName,new TypeToken<Map<String,String>>() {}.getType());
        Runnable run = new Runnable(){
            public void run(){
                try {
                    URL url = new URL(params.get("url"));
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    InputStream input = connection.getInputStream();
                    File outFile = new File("/sdcard/Download",params.get("fileName"));
                    FileOutputStream fout = new FileOutputStream(outFile);
                    byte[] buff = new byte[64*1024];
                    downloading.add(params.get("fileName"));
                    for (int k=0;(k=input.read(buff))>0;){
                        fout.write(buff,0,k);
                    }
                    fout.close();;
                    downloading.remove(params.get("fileName"));
                } catch (Throwable e) {
                    e.printStackTrace( );
                }
            }
        };
        CMClientHandler.executorService.execute(run);
        return "{\"fileName\":\""+params.get("fileName")+"\"}";
    }

    @Desc("list pictures")
    public static String listFiles(String args) {
        String[] files = new File("/sdcard/Download").list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
               return true;
            }
        });
        List<Map<String,String>> ret = new ArrayList<>();
        for (String str:files){
            File f = new File("/sdcard/DCIM/Camera",str);
            Map<String,String> fileDesc = new HashMap<>();
            fileDesc.put("fileName",f.getName());
            fileDesc.put("length",f.length()+"");
            if (downloading.contains(str))
                fileDesc.put("status","downloading");
            else
                fileDesc.put("status","done");
            ret.add(fileDesc);

        }
        return new Gson().toJson(ret);
    }

    @Desc("upload pictures")
    public static String uploadPic(final String args){
        final  String fileName = "IMG_"+(int)(Math.random()*10000000)+".jpg";
        Runnable run = new Runnable(){
          public void run(){
              try {
                  URL url = new URL(URLDecoder.decode(args));
                  HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                  InputStream input = connection.getInputStream();
                  File outFile = new File("/sdcard/DCIM/Camera",fileName);
                  FileOutputStream fout = new FileOutputStream(outFile);
                  byte[] buff = new byte[64*1024];
                  downloading.add(fileName);
                  for (int k=0;(k=input.read(buff))>0;){
                      fout.write(buff,0,k);
                  }
                  fout.close();;
                  downloading.remove(fileName);
              } catch (Throwable e) {
                  e.printStackTrace( );
              }
          }
        };
        CMClientHandler.executorService.execute(run);
        return "{\"fileName\":\""+fileName+"\"}";
    }
    @Desc("list pictures")
    public static String listPics(String args) {
        String[] files = new File("/sdcard/DCIM/Camera").list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                if (!name.endsWith(".jpg"))
                    return false;
                return true;
            }
        });
        List<Map<String,String>> ret = new ArrayList<>();
        for (String str:files){
            File f = new File("/sdcard/DCIM/Camera",str);
            Map<String,String> fileDesc = new HashMap<>();
            fileDesc.put("fileName",f.getName());
            fileDesc.put("length",f.length()+"");
            if (downloading.contains(str))
                fileDesc.put("status","downloading");
            else
                fileDesc.put("status","done");
            ret.add(fileDesc);

        }
        return new Gson().toJson(ret);
    }

    //调用API的地方
    public static String forward(String pkgName,String method,String arg){
        try {
            arg =  URLDecoder.decode(arg);
            Log.d("CMDManager","forward:"+pkgName+"."+method+"("+arg+")");
            if (pkgName.equals("system_process")){
                Log.d("CMDManager", "Service listens 1615!");
                YanCloudGet get = new YanCloudGet("127.0.0.1",1615);
                return get.get(pkgName,method,arg);
            }else {
                IBinder binder = DaemonService.instance.onBind(null);
                IAPIBinder server = IAPIBinder.Stub.asInterface(IAppConnector.Stub.asInterface(binder).request(pkgName, ""));
                Log.d("CMDManager", "Binder is alive:" + server.asBinder().isBinderAlive());
                return server.callMethod(method, arg);
            }
        } catch (Exception e) {
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(bo));
            return "{\"errorCode\":1, \"errorMessage\":\"" + bo.toString()
                    + "\"}";
        }
    }

    public static Method canHandle(String method) {
        try {
            Method m = CMDManager.class.getDeclaredMethod(method, String.class);
            if (m!=null && m.getAnnotation(Desc.class)!=null)
            return m;
            else return null;
        }catch(Exception e){
            return null;
        }
    }
    //调用API的地方，Forward
    public static String handle(String pkgName,String method, String arg){
        try {
            Method m = canHandle(method);
            if (m != null)
                return (String) m.invoke(null, arg);
            else return forward(pkgName, method, arg);
        }catch(Exception e){
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(bo));
            return bo.toString();
        }
    }

    @URLRegx("/CMDManager")
    public static void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {
        Multimap query = request.getQuery();
        String getMessage = query.getString("getMessage");
        GetMessage message = new Gson().fromJson(getMessage, GetMessage.class);
        response.send(CMDManager.handle(message.pkgName,message.method,message.arg));
    }
}


